#include "Entity.h"
#include "TestState.h"

//Constructor no parameters
TestEntity::TestEntity(){
    body = NULL;
    animated = false;
}

//Constructor for EDGE_POLYGON
TestEntity::TestEntity(b2World* world, b2Vec2 verts[4], int ent_type){

    animated = false;
    type = ent_type;
    position = sf::Vector2f(0,0);
    image = false;
    angle = 0;
    sides = 4;
    convex.setFillColor(sf::Color::Cyan);
    convex.setPointCount(sides);

    for(int i = 0; i < sides; i++){
        convex.setPoint(sides-1-i, sf::Vector2f(verts[i].x, verts[i].y));
    }

    Init(world);

}

//Constructor for Polygon
TestEntity::TestEntity(b2World* world, int ent_type){

    type = ent_type;
    position = sf::Vector2f(150,-200);

    image = false;

    sides = 5;

    convex.setFillColor(sf::Color::Cyan);
    convex.setPointCount(sides);

    convex.setPoint(4, sf::Vector2f(150,25));
    convex.setPoint(3, sf::Vector2f(100,100));
    convex.setPoint(2, sf::Vector2f(100,200));
    convex.setPoint(1, sf::Vector2f(200,300));
    convex.setPoint(0, sf::Vector2f(200,100));

    Init(world);

}

/**
Constructor for BOX & BALL, NO TEXTURE
@Param ent_type BOX or BALL, pos = x,y position, rotation in degrees ccw
@Param dim height/width radius/0 for BALL, file url of image = OPTIONAL
**/
TestEntity::TestEntity(b2World* world, int ent_type, sf::Vector2f pos, sf::Vector2f dim, float rotation){

    type = ent_type;
    position = pos;
    angle = rotation;
    image = false;
    eScale = 1.0;

    if (type == BALL) radius = dim.x;
    else {
        textureRect.width = dim.x;
        textureRect.height = dim.y;
    }
    Init(world);
}

/**
Constructor for BOX & BALL WITH TEXTURE
@Param ent_type BOX or BALL, pos = x,y position, rotation in degrees ccw
@Param for BALL, file url of image = OPTIONAL
**/
TestEntity::TestEntity(b2World* world, int ent_type, sf::Vector2f pos, char* file, float scale, float rotation){

    type = ent_type;
    position = pos;
    angle = rotation;
    image = true;
    eScale = scale;

    texture.loadFromFile(file);
    Init(world);
}

//initialize the body and fixture details
void TestEntity::Init(b2World* world){

    selected = false;
    animated = false;
    visible = true;
    doomed = false;
    m_numContacts = 0;

    //define body physics by type
    if(type == EDGE || type == EDGE_POLYGON){
        bodyDef.type = b2_staticBody;
    }
    else{
        bodyDef.type = b2_dynamicBody;
    }

    //define the body
    bodyDef.allowSleep = true;
    bodyDef.position = b2Vec2(position.x / SCALE, position.y / SCALE);
    bodyDef.angle = (b2_pi/180) * angle;
    body = world->CreateBody(&bodyDef);

    //define the body Fixture
    if(type == BOX || type == EDGE){

        FixtureDef.restitution = 0.2f;
        FixtureDef.density = 1.f;
        FixtureDef.friction = 0.5f;

        if(image){
            rect.setTexture(&texture);
            setTxRect(sf::IntRect(0,0,texture.getSize().x, texture.getSize().y));
        }else{
            //no action if no texture
        }

    }else if(type == BALL){

        FixtureDef.restitution = 0.6f;
        FixtureDef.density = 1.0f;
        FixtureDef.friction = 0.1f;

        if(image){
            circ.setTexture(&texture);
        }else{
            circ.setFillColor(sf::Color(255,0,255));
            circ.setOutlineThickness(-.5);
            circ.setOutlineColor(sf::Color(0, 0, 0));
        }

    }else if(type == POLYGON || type == EDGE_POLYGON){

        b2Vec2 vertices[sides];

        for( int i = 0; i < sides; i++){
            vertices[i].Set(convex.getPoint(i).x / SCALE, convex.getPoint(i).y / SCALE);
        }

        b2Shape.Set(vertices, sides); //pass array to the shape

        FixtureDef.density = 1.f;
        FixtureDef.friction = 0.7f;
        FixtureDef.shape = &b2Shape;
        body->CreateFixture(&FixtureDef);
    }

    setDimensions(eScale);
    body->SetUserData(this);
}

//returns a reference to the body of the entity
b2Body* TestEntity::getBody(){
    return body;
}

//Destructor
TestEntity::~TestEntity(){
    //delete the associated body
    if(body != NULL) body->GetWorld()->DestroyBody( body );
}

//update the Entity
void TestEntity::Update(){

    //update the animation
    if(animated){
        eAnimation.Update(eclock);
        int frame = eAnimation.getCurrentFrame();

        textureRect.left = (frame * textureRect.width);
        rect.setTextureRect(textureRect);
        circ.setTextureRect(textureRect);
    }

    //update position and angle of object
    if(type == POLYGON){
        convex.setPosition(body->GetPosition().x * SCALE, body->GetPosition().y * SCALE);
        convex.setRotation(180/b2_pi * body->GetAngle());
    }else if(type == BOX || type == EDGE){
        rect.setPosition(body->GetPosition().x * SCALE, body->GetPosition().y * SCALE);
        rect.setRotation(180/b2_pi * body->GetAngle());
    }else if(type == BALL){
        circ.setPosition(body->GetPosition().x * SCALE, body->GetPosition().y * SCALE);
        circ.setRotation(180/b2_pi * body->GetAngle());
    }

    //respond to collisions, if any
    collisionResponse();

    //change the color from yellow to red
    //test determines speed at which color transitions, slower = divide by higher #
    /*
    if(circleColor.b >0){
        float test = eclock.getElapsedTime().asMilliseconds()/100;
        circleColor.b -= test;
        if(circleColor.b != test && circleColor.b < 15) circleColor.b = 0;
        circle.setFillColor(circleColor);
    }
    */
}

// Render Method
void TestEntity::Render(sf::RenderWindow& window ){

    //draw the object only if visible == true
    if(visible){
        //RENDER BOX or EDGE
        if(type == BOX || type == EDGE){
            if(selected){
                rect.setOutlineThickness(5);
                rect.setOutlineColor(sf::Color::Green);
            }else{
                rect.setOutlineThickness(0);
            }
            window.draw(rect);

        //RENDER BALL
        }else if(type == BALL){
            window.draw(circ);

        //RENDER POLYGON
        }else if(type == POLYGON || type == EDGE_POLYGON){
            window.draw(convex);
        }
    }
}

//Returns the position in pixel (non-box2d) coordinates
sf::Vector2i TestEntity::getPixelPosition(){
     return sf::Vector2i(body->GetPosition().x * SCALE, body->GetPosition().y * SCALE);
}

//Swaps the visibility of the object
void TestEntity::ToggleVisible(){
    visible = !visible;
}

//move the Entity in the provided direction using impulse
void TestEntity::impulseMove(b2Vec2 vec){
    body->ApplyLinearImpulse( vec, body->GetWorldCenter() );
}

//move the Entity in the provided direction using force
void TestEntity::forceMove(b2Vec2 vec){
    body->ApplyForceToCenter(vec);
}

//Grow rect and b2body size based by scalar, update origin
void TestEntity::setDimensions(float scalar, float offset){
    if(type == BALL){
        if(image) radius = texture.getSize().x * scalar;
        circ.setRadius(radius);
        circ.setOrigin(radius, radius);
        b2circle.m_radius = radius / SCALE;
        FixtureDef.shape = &b2circle;
    }
    else if(type == BOX || type == EDGE){

        //change the dimensions if the entity is animated/image or non-animated image
        std::cout << "Old Rect: " << rect.getSize().x << " " << rect.getSize().y << std::endl;
        rect.setSize(sf::Vector2f(textureRect.width * scalar, textureRect.height * scalar));
        std::cout << "New Rect: " << rect.getSize().x << " " << rect.getSize().y << std::endl;
        rect.setOrigin(rect.getSize().x / 2, rect.getSize().y / 2);
        if(offset ==  0){
            b2Shape.SetAsBox( (rect.getSize().x / 2) / SCALE, (rect.getSize().y / 2) / SCALE);
        }else{
            b2Shape.SetAsBox( (rect.getSize().x * offset / 2) / SCALE, (rect.getSize().y/ 2) / SCALE);
        }
        FixtureDef.shape = &b2Shape;
    }

    b2Fixture *f = body->GetFixtureList();
    if(f != NULL) body->DestroyFixture(f);
    body->CreateFixture(&FixtureDef);
}

//toggle animated
void TestEntity::toggleAnimated(){
    animated = !animated;
    setDimensions(eScale);
    std::cout << "Animation Status: " << animated << std::endl;
}

void TestEntity::setTxRect(sf::IntRect iRect){
    textureRect = iRect;
}

//returns the type of the Entity
int TestEntity::getType(){
    return type;
}

//sets the frame rate for the entity
void TestEntity::setFrameRate(int fr){
    eAnimation.setFrameRate(fr);
}

//toggles animation oscillation;
void TestEntity::toggleOscillate(){
    eAnimation.toggleOscillate();
}

//toggle selection on/off
void TestEntity::toggleSelected(){
    selected = !selected;
}

//returns the status of selected
bool TestEntity::getSelectedStatus(){
    return selected;
}

int TestEntity::getCurrentFrame(){
    return eAnimation.getCurrentFrame();
}

void TestEntity::setCurrentFrame(int frame){
    eAnimation.setCurrentFrame(frame);
}

//respond to collisions
void TestEntity::collisionResponse(){

    if(!image){
        //change color when Entity contacts another
        if (m_numContacts >0){
            rect.setFillColor(sf::Color::Blue);
        }else{
            rect.setFillColor(sf::Color::Red );
        }
    }
}

//returns true if argument is contained within Entity rect
bool TestEntity::checkContains(sf::Vector2f vec){
    return rect.getGlobalBounds().contains(vec);
}
